// 一个小块每次移动的距离是固定的，为常量
var STEP = 20;

//分割容器，编程18行，10列
var ROW_COUNT = 18;
var COL_COUNT = 10;
// 创建每个模型的数据源
var MODELS = [
    // 第一个模型数据源（L型）
    {
        // 元素0
        0: {
            row: 2,
            col: 0
        },
        // 元素1
        1: {
            row: 2,
            col: 1
        },
        // 元素2
        2: {
            row: 2,
            col: 2
        },
        // 元素3
        3: {
            row: 1,
            col: 2
        }
    },
    // 第二个模型数据源,Z型
    {
        0: {
            row: 1,
            col: 1
        },
        1: {
            row: 1,
            col: 2
        },
        2: {
            row: 2,
            col: 2
        },
        3: {
            row: 2,
            col: 3
        }
    },
    // 第三个模型数据源，丁型
    {
        0: {
            row: 1,
            col: 1
        },
        1: {
            row: 0,
            col: 0
        },
        2: {
            row: 1,
            col: 0
        },
        3: {
            row: 2,
            col: 0
        }
    },

    // 第四个模型数据源，田型
    {
        0: {
            row: 1,
            col: 1
        },
        1: {
            row: 2,
            col: 1
        },
        2: {
            row: 2,
            col: 2
        },
        3: {
            row: 1,
            col: 2
        }
    },
    // 第五个模型数据源，一型
    {
        0: {
            row: 1,
            col: 0
        },
        1: {
            row: 1,
            col: 1
        },
        2: {
            row: 1,
            col: 2
        },
        3: {
            row: 1,
            col: 3
        }
    }
]
// 创建当前使用的一个模型
var currentModel = {
}

// 记录16宫格的位置
var currentX = 0;
var currentY = 0;

// 记录所有块元素的位置,利用键值对的形式k_v
var fixBlocks = {};

// 定时器
var myInterval = null;

// 入口方法,函数的调用都放在这里
function init() {
    // 获取keyCode
    onKeyDown()
    createModel()

}

// 根据模型的数据源来创建对应的块元素
function createModel() {
    // 在生成下一个模型之前来判断一下是否游戏结束
    if (isGameOver()) {
        gameOver();
        return;
    }
    // 初始化为第一个模型
    // currentModel = MODELS[0];

    // 要随机生成模型,可以使用lodash的内置函数
    currentModel = MODELS[_.random(0, MODELS.length - 1)];
    // 重新初始化模型的位置,很重要，把模型重新生成放在顶部
    currentX = 0;
    currentY = 0;
    // 生成模型对应的块元素

    for (var key in currentModel) {
        // 创建节点元素：创建一个div
        var divEle = document.createElement("div");
        divEle.className = "activity_model";
        // 把创建的元素放在第一个后面
        document.getElementById("container").appendChild(divEle);
    }
    locationBlocks();
    // 模型自动下落
    autoDown()
}

// 根据数据源定位块元素的位置
function locationBlocks() {
    // 调用 checkBound() 来判断块元素是否越界
    checkBound();
    // 1、拿到所有元素,自动创建一个类名均为activity_models的所有div元素的 一个数组
    var eles = document.getElementsByClassName("activity_model");
    for (var i = 0; i < eles.length; i++) {
        var activityModelEle = eles[i];
        // 2、找到每个块元素对应的数据（行和列）
        var blockModel = currentModel[i];
        // 3、根据每个每个块元素来指定位置
        // 每个块元素的位置由16宫格的位置和每个块元素在16宫格中的位置
        activityModelEle.style.top = (currentY + blockModel.row) * STEP + "px";
        activityModelEle.style.left = (currentX + blockModel.col) * STEP + "px";
    }
}

//获取用户的键盘事件，获取用户按下键盘的键值
function onKeyDown() {
    document.onkeydown = function (event) {
        // console.log(event.keyCode);
        switch (event.keyCode) {
            case 37: console.log("左");
                move(-1, 0);
                break;
            case 38: console.log("上");
                // move(0, -1);
                // 当按上的时候，进行旋转
                rotate();
                break;
            case 39: console.log("右");
                move(1, 0);
                break;
            case 40: console.log("下");
                move(0, 1);
                break;
        }
    }
}
function move(x, y) {
    // 往上移动，x为负，往下移动，x为正
    // 往左移动，y为负，往右移动，y为正
    // var activityModelEle = document.getElementsByClassName("activity_model")[0];
    // activityModelEle.style.top = parseInt(activityModelEle.style.top || 0) + y * STEP + "px";
    // activityModelEle.style.left = parseInt(activityModelEle.style.left || 0) + x * STEP + "px";

    // 解决移动触碰的问题
    // 如果移动之后发生触碰，则不需要进行移动
    if (isMeet((currentX + x), (currentY + y), currentModel)) {
        if (y != 0) {
            // 模型之间发生触碰，则把模型固定在当前位置
            fixBottomModel();
        }
        return;
    }
    // 16宫格在动
    currentX = currentX + x;
    currentY = currentY + y;
    locationBlocks();
}
// 旋转模型
function rotate() {
    // 克隆一下currentModel
    var cloneCurrentModel = _.cloneDeep(currentModel);


    // 遍历当前模型的数据源
    for (var key in cloneCurrentModel) {
        var blockModel = cloneCurrentModel[key];
        // 旋转后的行 = 旋转前的列；
        // 旋转后的列=3-旋转前的行；
        var temp = blockModel.row;
        blockModel.row = blockModel.col;
        blockModel.col = 3 - temp;
    }
    // 解决旋转触碰的问题
    // 如果旋转之后发生触碰，则不需要进行旋转
    if (isMeet(currentX, currentY, cloneCurrentModel)) {
        // 底部触碰，
        return;
    }
    currentModel = cloneCurrentModel;
    // 重新定位一下
    locationBlocks();
}
// 控制模型活动的范围
function checkBound() {
    var leftBound = 0;
    var rightBound = COL_COUNT;
    var bottomBound = ROW_COUNT;
    // 当元素超出边界之后，让16宫格后退一步
    for (var key in currentModel) {
        // 遍历块元素的数据
        var blockModel = currentModel[key];
        // 左侧越界
        if ((blockModel.col + currentX) < leftBound) {
            currentX++;
        }
        // 右侧越界 由于16宫格右边的一块没有用到，所以要大于等于右侧边界
        if ((blockModel.col + currentX) >= rightBound) {
            currentX--;
        }
        // 底侧越界
        if ((blockModel.row + currentY) >= bottomBound) {
            currentY--;
            fixBottomModel();
        }
    }
}

// 把模型固定在底部并修改样式
function fixBottomModel() {
    //1、 当在底部时改变模型样式
    var activityModelEles = document.getElementsByClassName("activity_model");
    for (var i = activityModelEles.length - 1; i >= 0; i--) {
        // 拿到每个元素
        var activityModelEle = activityModelEles[i];
        // 更改块元素的类名
        activityModelEle.className = "fix_model";
        // 把该元素放入变量中  这个地方不太懂，
        var blockModel = currentModel[i];
        fixBlocks[(currentY + blockModel.row) + "_" + (currentX + blockModel.col)] = activityModelEle;
    }
    // 判断是否有行被铺满
    isRemoveLine()

    // 2、创建新的模型
    createModel();
}

// 判断模型之间的触碰问题
// x,y 表示16宫格将要移动到的位置
// model表示当前模型数据源将要完成的变化
function isMeet(x, y, model) {
    // 触碰：在一个固定的位置已经存在一个被固定的块元素时，那么活动中的模型不能占用该位置
    // 判断触碰:如果将要移动到的位置已经存在一个块元素,返回true,反之返回false
    for (var k in model) {
        var blockModel = model[k];
        // 判断是否被占用
        if (fixBlocks[(y + blockModel.row) + "_" + (x + blockModel.col)]) {
            return true;
        }
    }
    return false;
}

// 判断一行是被铺满
function isRemoveLine() {
    // 在一行中，如果每一列都存在块元素，那么需要清理该行
    // 所以遍历所有行中的所有列    
    // 接下来遍历所有行
    for (var i = 0; i < ROW_COUNT; i++) {
        // 遍历当前行的所有列
        // 添加标记符，先假设改行被铺满
        var flag = true;
        for (var j = 0; j < COL_COUNT; j++) {
            // 如果不能取出对应的行和列对应的坐标，则未被铺满
            if (!fixBlocks[i + "_" + j]) {
                flag = false;
                break;
            }
        }

        if (flag == true) {
            // 该行被铺满
            console.log("该行被铺满");
            removeLine(i);
        }
    }
}

// 清理被铺满的一行
// 传入当前行的行数line
function removeLine(line) {
    // 1、删除该行中的所有块元素
    // 2、删除改行中所有块元素的数据源

    // 遍历行中的所有列
    for (var j = 0; j < COL_COUNT; j++) {
        // 1、删除该行中的所有块元素
        document.getElementById("container").removeChild(fixBlocks[line + "_" + j])
        // 2、删除改行中所有块元素的数据源
        fixBlocks[line + "_" + j] = null;
    }
    downLine(line);
}

// 让被清理行之上的块元素下落
function downLine(line) {
    // 遍历该行之上的所有行
    for (var i = line - 1; i >= 0; i--) {
        // 遍历当前中的所有列
        for (var j = 0; j < COL_COUNT; j++) {
            if (!fixBlocks[i + "_" + j])
                // 若当前行中的当前列没有不存在数据，则跳出此次迭代，用continue
                continue;
            // 若当前行中的当前列没有存在数据，
            // 则让被清理行之上的所有块元素数据源的行数 + 1
            fixBlocks[(i + 1) + "_" + j] = fixBlocks[i + "_" + j]
            // 让块元素在容器中的位置下落
            fixBlocks[(i + 1) + "_" + j].style.top = (i + 1) * STEP + "px";
            // 清理掉之前的块元素
            fixBlocks[i + "_" + j] = null;
        }
    }
}

// 模型自动下落
function autoDown() {
    // 清除定时器
    if (myInterval) {
        clearInterval(myInterval);
    }
    myInterval = setInterval(function () {
        move(0, 1);
    }, 600)
    // 每隔600ms调用这个匿名函数，让自动下落
}
// 判断游戏是否结束
function isGameOver() {
    // 当第0行存在块元素时，游戏结束
    for (var j = 0; j < COL_COUNT; j++) {
        if (fixBlocks["0_" + j]) {
            return true;
        }
    }
    return false;
}
// 结束游戏
function gameOver() {
    // 1、停止定时器
    if (myInterval) {
        clearInterval(myInterval);
    }
    // 2、弹出结束标志
    alert("抱歉，GAME OVER");
}
